﻿using EPiAbstractions.Community.Blog;
using EPiServer.Common;
using EPiServer.Common.Security;
using EPiServer.Community.Blog;
using JoelAbrahamsson.TestUtilities;
using Moq;
using StructureMap.AutoMocking;
using Xunit;

namespace JoelAbrahamsson.MetaWeblog.Tests
{
    public class UpdatePostTests
    {
        [Fact]
        public void UpdatePost_ValidatesUser()
        {
            string username = RandomValues.String;
            string password = RandomValues.String;
            Blog postsBlog = CreateFakeBlog();
            Mock<Entry> fakeExistingEntry = CreateFakeEntry(postsBlog);
            Mock<IBlogHandlerFacade> fakeBlogHandlerFacade = CreateFakeBlogHandlerFacade();
            Mock<MetaWeblogHandler> partiallyFakedMetaWeblogHandler =
                CreatePartiallyFakedMetaWeblogHandler(postsBlog, fakeExistingEntry.Object.ID, fakeExistingEntry, fakeBlogHandlerFacade, null);
            Post updatedPost = new Post { title = RandomValues.String, description = RandomValues.String };

            partiallyFakedMetaWeblogHandler.Object.UpdatePost(
                    fakeExistingEntry.Object.ID.ToString(), username, password, updatedPost, true);

            partiallyFakedMetaWeblogHandler.Verify(handler => handler.GetValidatedUser(It.IsAny<Blog>(), username, password));
        }

        private Blog CreateFakeBlog()
        {
            return new Mock<Blog>().Object;
        }

        private Mock<Entry> CreateFakeEntry(Blog postsBlog)
        {
            var fakeEntry = new Mock<Entry>();
            fakeEntry.SetupGet(entry => entry.ID).Returns(1);
            fakeEntry.SetupGet(entry => entry.Blog).Returns(postsBlog);
            fakeEntry.Setup(entry => entry.Clone()).Returns(fakeEntry.Object);
            fakeEntry.CallBase = true;
            return fakeEntry;
        }

        private Mock<IBlogHandlerFacade> CreateFakeBlogHandlerFacade()
        {
            var fakeBlogHandlerFacade = new Mock<IBlogHandlerFacade>();
            fakeBlogHandlerFacade.Setup(facade => facade.UpdateEntry(It.IsAny<Entry>()));
            return fakeBlogHandlerFacade;
        }

        private Mock<MetaWeblogHandler> CreatePartiallyFakedMetaWeblogHandler(
            Blog postsBlog, int postID, Mock<Entry> fakeExistingEntry, Mock<IBlogHandlerFacade> fakeBlogHandlerFacade, IUser fakeUser)
        {
            MoqAutoMocker<MetaWeblogHandler> metaWebLogHandlerAutoMocker = new MoqAutoMocker<MetaWeblogHandler>();
            metaWebLogHandlerAutoMocker.Inject(fakeBlogHandlerFacade.Object);
            metaWebLogHandlerAutoMocker.PartialMockTheClassUnderTest();
            Mock<MetaWeblogHandler> partiallyFakedMetaWeblogHandler = Mock.Get(metaWebLogHandlerAutoMocker.ClassUnderTest);
            partiallyFakedMetaWeblogHandler.Setup(handler => handler.GetEntry(postID.ToString())).Returns(fakeExistingEntry.Object);
            partiallyFakedMetaWeblogHandler.Setup(
                handler => handler.GetValidatedUser(postsBlog, It.IsAny<string>(), It.IsAny<string>())
                ).Returns(fakeUser);
            partiallyFakedMetaWeblogHandler.Setup(handler => handler.UpdateEntryCategories(It.IsAny<Entry>(), It.IsAny<string[]>()));
            partiallyFakedMetaWeblogHandler.Setup(handler => handler.GetOrCreateUserAuthor(It.IsAny<IUser>()));
            partiallyFakedMetaWeblogHandler.Setup(handler => handler.UpdateEntryTags(It.IsAny<Entry>(), It.IsAny<string>(), It.IsAny<IAuthor>()));
            return partiallyFakedMetaWeblogHandler;
        }

        [Fact]
        public void GivenPost_UpdatePost_UpdatesBlogEntry()
        {
            Blog postsBlog = CreateFakeBlog();
            Mock<Entry> fakeExistingEntry = CreateFakeEntry(postsBlog);
            Mock<IBlogHandlerFacade> fakeBlogHandlerFacade = CreateFakeBlogHandlerFacade();
            IUser fakeUser = CreateFakeUser();
            Mock<MetaWeblogHandler> partiallyFakedMetaWeblogHandler = 
                CreatePartiallyFakedMetaWeblogHandler(postsBlog, fakeExistingEntry.Object.ID, fakeExistingEntry, fakeBlogHandlerFacade, fakeUser);
            Post updatedPost = new Post { title = RandomValues.String, description = RandomValues.String };

            bool returnedResult = partiallyFakedMetaWeblogHandler.Object.UpdatePost(
                fakeExistingEntry.Object.ID.ToString(), RandomValues.String, RandomValues.String, updatedPost, true);
        
            fakeBlogHandlerFacade.Verify(facade => facade.UpdateEntry(It.IsAny<Entry>()));
            fakeBlogHandlerFacade.Verify(facade => facade.UpdateEntry(It.Is<Entry>(entry => entry.Title == updatedPost.title)));
            fakeBlogHandlerFacade.Verify(facade => facade.UpdateEntry(It.Is<Entry>(entry => entry.Content == updatedPost.description)));
            Assert.True(returnedResult, "Expected UpdatePost to return true but it returned false");
        }

        private IUser CreateFakeUser()
        {
            return new Mock<IUser>().Object;
        }

        [Fact]
        public void GivenPost_UpdatePost_SetsEntryProperties()
        {
            Blog postsBlog = CreateFakeBlog();
            Mock<Entry> fakeExistingEntry = CreateFakeEntry(postsBlog);
            Mock<IBlogHandlerFacade> fakeBlogHandlerFacade = CreateFakeBlogHandlerFacade();
            IUser fakeUser = CreateFakeUser();
            Mock<MetaWeblogHandler> partiallyFakedMetaWeblogHandler =
                CreatePartiallyFakedMetaWeblogHandler(postsBlog, fakeExistingEntry.Object.ID, fakeExistingEntry, fakeBlogHandlerFacade, fakeUser);
            Post updatedPost = new Post { title = RandomValues.String, description = RandomValues.String, categories = new string[0]};

            partiallyFakedMetaWeblogHandler.Object.UpdatePost(
                fakeExistingEntry.Object.ID.ToString(), RandomValues.String, RandomValues.String, updatedPost, true);

            partiallyFakedMetaWeblogHandler.Verify(handler=> handler.SetEntryProperties(fakeExistingEntry.Object, updatedPost, It.IsAny<IAuthor>()));
        }
    }
}
